home *** CD-ROM | disk | FTP | other *** search
/ Aminet 45 / Aminet 45 (2001)(GTI - Schatztruhe)[!][Oct 2001].iso / Aminet / dev / gui / FoxGuiSource.lha / FoxLibSource / FoxGuiTools.c < prev    next >
C/C++ Source or Header  |  2001-07-07  |  30KB  |  977 lines

  1. /* FoxGUI - The fast, flexible, free Amiga GUI system
  2.     Copyright (C) 2001 Simon Fox (Foxysoft)
  3.  
  4. This library is free software; you can redistribute it and/ormodify it under the terms of the GNU Lesser General PublicLicense as published by the Free Software Foundation; eitherversion 2.1 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNULesser General Public License for more details.You should have received a copy of the GNU Lesser General PublicLicense along with this library; if not, write to the Free SoftwareFoundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  5. Foxysoft: www.foxysoft.co.uk      Email:simon@foxysoft.co.uk                */
  6.  
  7. /******************************************************************************
  8.  * Shared library code.  Cannot call functions which use exit() such as:
  9.  * printf(), fprintf()
  10.  *
  11.  * Otherwise:
  12.  * The linker returns "__XCEXIT undefined" and the program will fail.
  13.  * This is because you must not exit() a library!
  14.  *
  15.  * Also:
  16.  * proto/exec.h must be included instead of clib/exec_protos.h and
  17.  * __USE_SYSBASE must be defined.
  18.  *
  19.  * Otherwise:
  20.  * The linker returns "Absolute reference to symbol _SysBase" and the
  21.  * library crashes.  Presumably the same is true for the other protos.
  22.  ******************************************************************************/
  23.  
  24. #define __USE_SYSBASE
  25.  
  26. #include <proto/mathieeedoubbas.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #include <math.h>
  30.  
  31. #include <proto/graphics.h>
  32. #include <proto/intuition.h>
  33. #include <proto/layers.h>
  34. #include <proto/exec.h>
  35. #include <devices/input.h>
  36. #include <clib/alib_protos.h>
  37. #include "/FoxInclude/foxgui.h"
  38. #include "FoxGuiTools.h"
  39.  
  40. /****************************************************************************************************
  41.  * This module is for functions that are very general in nature and of potential use to more than   *
  42.  * one gui module.                                                                                  *
  43.  ****************************************************************************************************/
  44.  
  45. /****************************************************************************************************
  46.  * Note that all functions use absolute (far) addressing rather than base relative (near)           *
  47.  * addressing.  This is currently necessary because Characters.c calls the MakeBevel() function     *
  48.  * directly which prevents the linker from moving this module away from Characters.o in memory.     *
  49.  * When Characters.c no-longer calls MakeBevel() it may not be necessary for these to be declared   *
  50.  * far.                                                                                             *
  51.  ****************************************************************************************************/
  52.  
  53. #define RAWKEY_RETURN 0x0044
  54.  
  55. typedef struct GuiAllocStruct
  56.     {
  57.     /*    It's important that the header is 4 bytes long.  If it's 3 bytes then the unsigned after the
  58.         header gets word-aligned making it impossible to calculate where the header starts. */
  59.     char header[4];
  60.     unsigned AllocSize[1];
  61.     /* After this comes the actual memory allocation and then the footer.  Since the size is
  62.         unknown we can't define the rest explicitly */
  63.     } GuiAlloc;
  64.  
  65. static char *FindHeader(void *AllocStart)
  66.     {
  67.     register char *header;
  68.     register unsigned *AllocSize = (unsigned *) AllocStart;
  69.     AllocSize = &(AllocSize[-1]);
  70.     header = (char *) AllocSize;
  71.     return &(header[-4]);
  72.     }
  73.  
  74. static char *FindFooter(GuiAlloc *p)
  75.     {
  76.     register char *AllocStart = (char *) &(p->AllocSize[1]);
  77.     return &(AllocStart[p->AllocSize[0]]);
  78.     }
  79.  
  80. void* FOXLIB GuiMalloc(REGD0 unsigned NoOfBytes, REGD1 unsigned long flags)
  81.     {
  82.     register void *retval = NULL;
  83.     register GuiAlloc *p;
  84.  
  85.     if (FastMallocs)
  86.         {
  87.         if (flags & MEMF_CLEAR)
  88.             return calloc(1, NoOfBytes);
  89.         else
  90.             return malloc(NoOfBytes);
  91.         }
  92.  
  93.     if (flags & MEMF_CLEAR)
  94.         p = (GuiAlloc *) calloc(1, sizeof(GuiAlloc) + NoOfBytes + (4 * sizeof(char)));
  95.     else
  96.         p = (GuiAlloc *) malloc(sizeof(GuiAlloc) + NoOfBytes + (4 * sizeof(char)));
  97.     if (p)
  98.         {
  99.         register char *footer;
  100.         retval = (void *) &(p->AllocSize[1]); // A pointer to the first byte after the AllocSize.
  101.         p->header[0] = 'F';
  102.         p->header[1] = 'o';
  103.         p->header[2] = 'x';
  104.         p->header[3] = 'y';
  105.         p->AllocSize[0] = NoOfBytes;
  106.  
  107.         // Find the footer
  108.         footer = FindFooter(p);
  109.         footer[0] = 'G';
  110.         footer[1] = 'u';
  111.         footer[2] = 'i';
  112.         footer[3] = '!';
  113.  
  114.         Gui.NumAllocs++;
  115.         }
  116.     return retval;
  117.     }
  118.  
  119. /* This function is publically available (i.e. is currently prototyped in foxgui.h rather than
  120.     guisys.h) but is not currently documented in FoxGui.guide because it would be unwise to let users
  121.     get their hands on this when in FAST_MALLOCS mode it always returns TRUE! */
  122.  
  123. BOOL FOXLIB WasGuiMallocd(REGA0 void *p)
  124.     {
  125.     if (p)
  126.         {
  127.         register char *footer, *header;
  128.  
  129.         if (FastMallocs)
  130.             return TRUE;
  131.  
  132.         header = FindHeader(p);
  133.         if (strncmp(header, "Foxy", 4))
  134.             return FALSE;
  135.         footer = FindFooter((GuiAlloc *) header);
  136.         if (strncmp(footer, "Gui!", 4))
  137.             return FALSE;
  138.         return TRUE;
  139.         }
  140.     return FALSE;
  141.     }
  142.  
  143. void FOXLIB GuiFreeMem(REGA0 void *p, REGD0 int line, REGA1 char *fname)
  144.     {
  145.     char ErrMsg[255];
  146.     if (p)
  147.         {
  148.         if (FastMallocs)
  149.             {
  150.             free(p);
  151.             return;
  152.             }
  153.  
  154.         if (WasGuiMallocd(p))
  155.             {
  156.             register GuiAlloc *header = (GuiAlloc *) FindHeader(p);
  157.             /*    Remove the F of Foxy from the header so that multiple attempts to free the same piece of
  158.                 memory will fail. */
  159.             header->header[0] = 0;
  160.             free(header);
  161.             Gui.NumAllocs--;
  162.             }
  163.         else
  164.             {
  165.             sprintf(ErrMsg, "Attempt to free an unallocated or currupted pointer at line %d in %s", line, fname);
  166.             SetLastErrAndLine(ErrMsg,line);
  167.             }
  168.         }
  169.     else
  170.         {
  171.         sprintf(ErrMsg, "Attempt to free a NULL pointer at line %d in %s", line, fname);
  172.         SetLastErrAndLine(ErrMsg,line);
  173.         }
  174.     }
  175.  
  176. BOOL OFFSET Diagnostic(char *fn, short enter, BOOL succeed)
  177.    {
  178. #ifdef DIAGNOSTICS
  179.    static int spc = 0;
  180.    int l;
  181.    char string[100];
  182.    strcpy(string, "");
  183. #ifndef DEBUG
  184.     // if we're not in DEBUG mode then only do the following code when a debug file
  185.     // has been specified.
  186.    if (Gui.DebugFile)
  187.       {
  188. #endif
  189.       if (!enter)
  190.          spc -= 3;
  191.       for (l = 0; l < spc; l++)
  192.          strcat(string, " ");
  193.       strcat(string, fn);
  194.       if (enter)
  195.          {
  196.          strcat(string, " started");
  197.          spc += 3;
  198.          }
  199.       else
  200.          {
  201.          strcat(string, " finished ");
  202.          if (succeed)
  203.             strcat(string, "successfully");
  204.          else
  205.             strcat(string, "unsuccessfully");
  206.          }
  207.       strcat(string, ".\n");
  208.       if (spc == 0 && !enter)
  209.          strcat(string, "\n");
  210. #ifndef DEBUG
  211.       }
  212. #endif
  213.    GuiReportError(string, GUI_WARNING);
  214. #endif
  215.    return succeed;
  216.    }
  217.  
  218. /* This function works out the background colour for a control by looking at the parent window or
  219.     frame.  It may also be necessary to look at the parent's parent etc so this function is recursive. */
  220. BYTE OFFSET GetBackCol(void *Parent)
  221.     {
  222.     if (ISGUIWINDOW(Parent))
  223.         return ((GuiWindow *) Parent)->Win->RPort->BgPen;
  224.     else // The parent is a frame
  225.         if (((Frame *) Parent)->WidgetData->flags & FM_CLEAR)
  226.             return GetBackCol(((Frame *) Parent)->WidgetData->Parent);
  227.         else
  228.             return (BYTE) ((Frame *) Parent)->light.BackPen;
  229.     }
  230.  
  231. BOOL OFFSET GadInWinList(struct Gadget *gad, struct Window *w)
  232.     {
  233.     struct Gadget *g = w->FirstGadget;
  234.  
  235.     while (g)
  236.         {
  237.         if (g == gad)
  238.             return TRUE;
  239.         g = g->NextGadget;
  240.         }
  241.     return FALSE;
  242.     }
  243.  
  244. void OFFSET EnableGadget(struct Gadget *gad, struct Window *win, BOOL redraw)
  245.     {
  246.     // This is an alternative to calling intuitions OnGadget() because
  247.     // OnGadget() will refresh not only the gadget that you enable but also
  248.     // all gadgets that appear after it in the gadget list for that window.
  249.     // If there are lots of gadgets in the window, OnGadget() can be VERY
  250.     // slow.  This only refreshes the gadget that you are enabling.
  251.     UWORD GadPos = RemoveGList(win, gad, 1L);
  252.     // Occasionally, the call to RemoveGList (above) fails.  This only appears to
  253.     // happen during the call to RestoreSysStatus() when GuiMessage() is closing it's
  254.     // window and only for the first three gadgets that it tries to enable.  Since
  255.     // these three gadgets fail to disable in the first place, it doesn't really
  256.     // matter.  I don't know why this happens or which those gadgets are but it
  257.     // doesn't matter as long as we don't add back the gadget that we failed to
  258.     // remove from the gadget list.
  259.     if (GadPos != -1)
  260.         {
  261.         gad->Flags ^= GFLG_DISABLED;
  262.         AddGList(win, gad, (unsigned long) GadPos, 1L, NULL);
  263.         if (redraw)
  264.             RefreshGList(gad, win, NULL, 1L);
  265.         }
  266.     }
  267.  
  268. void OFFSET DisableGadget(struct Gadget *gad, struct Window *win, BOOL redraw)
  269.     {
  270.     // This is an alternative to calling intuitions OffGadget() because
  271.     // OffGadget() will refresh not only the gadget that you disable but also
  272.     // all gadgets that appear after it in the gadget list for that window.
  273.     // If there are lots of gadgets in the window, OffGadget() can be VERY
  274.     // slow.  This only refreshes the gadget that you are disabling.
  275.     UWORD GadPos = RemoveGList(win, gad, 1L);
  276.     // Occasionally, the call to RemoveGList (above) fails.  This only appears to
  277.     // happen during the call to DisableEverything() when GuiMessage() is opening it's
  278.     // window and only for the last three gadgets that it tries to disable.  The
  279.     // call to EnableGadget fails similarly for the same three gadgets.  I don't
  280.     // know why this happens or which those gadgets are but it doesn't matter as
  281.     // long as we don't add back the gadget that we failed to remove from the gadget
  282.     // list.
  283.     if (GadPos != -1)
  284.         {
  285.         gad->Flags |= GFLG_DISABLED;
  286.         AddGList(win, gad, (unsigned long) GadPos, 1L, NULL);
  287.         if (redraw)
  288.             RefreshGList(gad, win, NULL, 1L);
  289.         }
  290.     }
  291.  
  292. void OFFSET UnclipGuiWindow(GuiWindow *gw)
  293.     {
  294.     struct Region *OldRegion;
  295.  
  296.     if (OldRegion = InstallClipRegion(gw->Win->WLayer, NULL))
  297.         DisposeRegion(OldRegion);
  298.     }
  299.  
  300. struct Region OFFSET *ClipGuiWindow(GuiWindow *gw, long minx, long miny, long maxx, long maxy)
  301.     {
  302.     struct Region *NewReg;
  303.     struct Rectangle rect;
  304.  
  305.     rect.MinX = minx;
  306.     rect.MinY = miny;
  307.     rect.MaxX = maxx;
  308.     rect.MaxY = maxy;
  309.  
  310.     if (NewReg = NewRegion())
  311.         if (!OrRectRegion(NewReg, &rect))
  312.             {
  313.             DisposeRegion(NewReg);
  314.             NewReg = NULL;
  315.             }
  316.     return InstallClipRegion(gw->Win->WLayer, NewReg);
  317.     }
  318.  
  319. TreeItem* FindPreviousItem(TreeItem *ti)
  320. {
  321.     TreeItem *p;
  322.  
  323.     // Traverse the list until we find the previous item.
  324.     if (ti->parent)
  325.     {
  326.         p = ti->parent;
  327.         if (p->firstchild != ti)
  328.         {
  329.             p = p->firstchild;
  330.             while (p->next != ti)
  331.                 p = p->next;
  332.             while ((p->firstchild && (p->flags & TI_OPEN)) || (p->next && p->next != ti))
  333.                 if (p->next && p->next != ti)
  334.                     p = p->next;
  335.                 else
  336.                     p = p->firstchild;
  337.         }
  338.     }
  339.     else if (ti == ti->treecontrol->itemlist)
  340.         return NULL;
  341.     else
  342.     {
  343.         if (p = ti->treecontrol->itemlist)
  344.         {
  345.             while (p->next != ti)
  346.                 p = p->next;
  347.             while ((p->firstchild && (p->flags & TI_OPEN)) || (p->next && p->next != ti))
  348.                 if (p->next && p->next != ti)
  349.                     p = p->next;
  350.                 else
  351.                     p = p->firstchild;
  352.         }
  353.     }
  354.     return p;
  355. }
  356.  
  357. void FreeItemTree(TreeItem *ti, TreeItem *masterparent, BOOL refresh)
  358. {
  359.     if (ti->firstchild)
  360.         FreeItemTree(ti->firstchild, masterparent, refresh);
  361.     if (ti->next)
  362.         FreeItemTree(ti->next, masterparent, refresh);
  363.     if (ti->treecontrol->topshown == ti)
  364.         if (masterparent)
  365.             ti->treecontrol->topshown = FindPreviousItem(masterparent);
  366.         else
  367.             ti->treecontrol->topshown = NULL;
  368.     if (ti->bmi)
  369.         GuiFree(ti->bmi);
  370.     // Don't free the bitmap unless we allocated a copy - otherwise the user allocated it and should free
  371.     //    it.  In this way, the user can use the same bitmap for many items in the list.
  372.     if (ti->bm && (ti->flags & TI_BITMAPISSCALED))
  373.         FreeGuiBitMap(ti->bm);
  374.     if (ti->it.IText)
  375.         GuiFree(ti->it.IText);
  376.     if (ti->plusminus)
  377.     {
  378.         ti->plusminus->WidgetData->ParentControl = NULL;
  379.         DestroyButton(ti->plusminus, refresh);
  380.     }
  381.     if (ti == ti->treecontrol->hiitem)
  382.         ti->treecontrol->hiitem = NULL;
  383.     GuiFree(ti);
  384. }
  385.  
  386. static int FindTop(TreeItem *root, TreeItem *SearchFor, int CurTop, BOOL *found)
  387. {
  388.     if (root != SearchFor)
  389.     {
  390.         if (root->firstchild && (root->flags & TI_OPEN))
  391.             CurTop = FindTop(root->firstchild, SearchFor, CurTop + root->it.ITextFont->ta_YSize, found);
  392.         if (root->next && !*found)
  393.             CurTop = FindTop(root->next, SearchFor, CurTop + root->it.ITextFont->ta_YSize, found);
  394.     }
  395.     else
  396.         *found = TRUE;
  397.     return CurTop;
  398. }
  399.  
  400. int OFFSET CalcItemTop(TreeItem *ti)
  401. {
  402.     BOOL found1 = FALSE, found2 = FALSE;
  403.     if (ti)
  404.         return FindTop(ti->treecontrol->itemlist, ti, 0, &found1) - FindTop(ti->treecontrol->itemlist, ti->treecontrol->topshown, 0, &found2);
  405.     else
  406.         return 0;
  407. }
  408.  
  409. void OFFSET FindMaxSizes(TreeItem *root, int *maxlen, int *maxtop, int *top)
  410. {
  411.     int length;
  412.  
  413.     if (!root)
  414.         return;
  415.  
  416.     length = IntuiTextLength(&root->it) + root->it.LeftEdge;
  417.  
  418.     if (length > *maxlen)
  419.         *maxlen = length;
  420.     if (*top > *maxtop)
  421.         *maxtop = *top;
  422.  
  423.     if (root->firstchild && (root->flags & TI_OPEN))
  424.     {
  425.         *top += root->it.ITextFont->ta_YSize;
  426.         FindMaxSizes(root->firstchild, maxlen, maxtop, top);
  427.     }
  428.     if (root->next)
  429.     {
  430.         *top += root->it.ITextFont->ta_YSize;
  431.         FindMaxSizes(root->next, maxlen, maxtop, top);
  432.     }
  433. }
  434.  
  435. void OFFSET DestroyVerticalScroller(ListBox *lb, BOOL refresh)
  436.     {
  437.     if (lb->UD)
  438.         {
  439.         if (lb->UD->ScrollUp)
  440.             {
  441.             // We have to pretend the button is not part of a list box or we can't destroy it!
  442.             lb->UD->ScrollUp->WidgetData->ParentControl = NULL;
  443.             DestroyButton(lb->UD->ScrollUp, FALSE);
  444.             }
  445.         if (lb->UD->ScrollDown)
  446.             {
  447.             // We have to pretend the button is not part of a list box or we can't destroy it!
  448.             lb->UD->ScrollDown->WidgetData->ParentControl = NULL;
  449.             DestroyButton(lb->UD->ScrollDown, FALSE);
  450.             }
  451.  
  452.         if (lb->Enabled)
  453.             RemoveGadget(lb->Win->Win, &lb->UD->ScrollGad);
  454.  
  455.         GuiFree(lb->UD);
  456.         lb->UD = NULL;
  457.         lb->WidgetData->flags &= ~SYS_LB_VSCROLL;
  458.         if (lb->LR)
  459.             lb->DarkBorder.NextBorder = &lb->LR->LightBorder;
  460.         else
  461.             lb->DarkBorder.NextBorder = NULL;
  462.  
  463.         ResizeListBox(lb, lb->WidgetData->left, lb->WidgetData->top, lb->WidgetData->width, lb->WidgetData->height, (double) 1.0, (double) 1.0, refresh);
  464.         if (refresh)
  465.             ListBoxRefresh(lb);
  466.         }
  467.     }
  468.  
  469. void OFFSET DestroyHorizontalScroller(ListBox *lb, BOOL refresh)
  470.     {
  471.     if (lb->LR)
  472.         {
  473.         if (lb->LR->ScrollUp)
  474.             {
  475.             // We have to pretend the button is not part of a list box or we can't destroy it!
  476.             lb->LR->ScrollUp->WidgetData->ParentControl = NULL;
  477.             DestroyButton(lb->LR->ScrollUp, FALSE);
  478.             }
  479.         if (lb->LR->ScrollDown)
  480.             {
  481.             // We have to pretend the button is not part of a list box or we can't destroy it!
  482.             lb->LR->ScrollDown->WidgetData->ParentControl = NULL;
  483.             DestroyButton(lb->LR->ScrollDown, FALSE);
  484.             }
  485.  
  486.         if (lb->Enabled)
  487.             RemoveGadget(lb->Win->Win, &lb->LR->ScrollGad);
  488.  
  489.         GuiFree(lb->LR);
  490.         lb->LR = NULL;
  491.         lb->WidgetData->flags &= ~SYS_LB_HSCROLL;
  492.         lb->xOffset = 0;
  493.         if (lb->UD)
  494.             lb->UD->DarkBorder.NextBorder = NULL;
  495.         else
  496.             lb->DarkBorder.NextBorder = NULL;
  497.  
  498.         ResizeListBox(lb, lb->WidgetData->left, lb->WidgetData->top, lb->WidgetData->width, lb->WidgetData->height, (double) 1.0, (double) 1.0, refresh);
  499.         if (refresh)
  500.             ListBoxRefresh(lb);
  501.         }
  502.     }
  503.  
  504. void OFFSET ResizeHorizontalScroller(ListBox *lb, int x, int y, int width, int height, double xfactor, double yfactor, BOOL eraseold)
  505.     {
  506.     short ButtonWidthMult = 1;
  507.     UWORD GadPos = (unsigned short) -1;
  508.  
  509.     if (lb->WidgetData->flags & SYS_LB_VSCROLL)
  510.         ButtonWidthMult = 2;
  511.  
  512.     if (eraseold)
  513.         {
  514.         ResizeButton(lb->LR->ScrollDown, x, y + height - SCROLL_BUTTON_HEIGHT - 1, lb->LR->ScrollDown->button.Width, lb->LR->ScrollDown->button.Height, lb->LR->ScrollDown->hidden == 0);
  515.         ResizeButton(lb->LR->ScrollUp, x + width - (ButtonWidthMult * SCROLL_BUTTON_WIDTH), y + height - SCROLL_BUTTON_HEIGHT - 1, lb->LR->ScrollUp->button.Width, lb->LR->ScrollUp->button.Height, lb->LR->ScrollUp->hidden == 0);
  516.         // returns -1 for failure.
  517.         GadPos = RemoveGList(lb->Win->Win, &lb->LR->ScrollGad, 1L);
  518.         }
  519.  
  520.     /*    Remember when reading the numbers below that the full width of the list box
  521.         is width which means that it goes from left to width-1.  Similarly, it goes
  522.         from top to height-1 */
  523.  
  524.     MakeBevel(&lb->LR->LightBorder, &lb->LR->DarkBorder, lb->LR->points, x + SCROLL_BUTTON_WIDTH,
  525.             y + height - SCROLL_BUTTON_HEIGHT - 1, width - ((ButtonWidthMult + 1) *
  526.             SCROLL_BUTTON_WIDTH), SCROLL_BUTTON_HEIGHT, TRUE);
  527.  
  528.     lb->LR->ScrollGad.LeftEdge = x + SCROLL_BUTTON_WIDTH + 3;
  529.     lb->LR->ScrollGad.TopEdge = y + height - SCROLL_BUTTON_HEIGHT + 1;
  530.     lb->LR->ScrollGad.Width = width - ((ButtonWidthMult + 1) * SCROLL_BUTTON_WIDTH) - 6;
  531.     if (GadPos != -1)
  532.         AddGList(lb->Win->Win, &lb->LR->ScrollGad, (unsigned long) GadPos, 1L, NULL);
  533.     }
  534.  
  535. void OFFSET MakeVerticalScroller(ListBox *lb, int (*ScrollUpFn)(PushButton*), int (*ScrollDownFn)(PushButton*))
  536.     {
  537.     int InitialLeft, InitialTop;
  538.     int ButtonFlags = BN_CLEAR | BN_AR;
  539.  
  540.     if (!lb)
  541.         return;
  542.     if (lb->UD)
  543.         return;
  544.     if ((lb->UD = (Scroller*) GuiMalloc(sizeof(Scroller), MEMF_CLEAR)) == NULL)
  545.         return;
  546.  
  547.     if (!ISGUIWINDOW(lb->WidgetData->Parent))
  548.         {
  549.         Frame *ParentFrame = (Frame *) lb->WidgetData->Parent;
  550.         InitialLeft = lb->WidgetData->left - ParentFrame->button.LeftEdge;
  551.         InitialTop = lb->WidgetData->top - ParentFrame->button.TopEdge;
  552.         }
  553.     else
  554.         {
  555.         InitialLeft = lb->WidgetData->left;
  556.         InitialTop = lb->WidgetData->top;
  557.         }
  558.  
  559.     if (lb->hidden == 1)
  560.         ButtonFlags |= SYS_BN_HIDDEN;
  561.     lb->UD->ScrollDown = MakeButton(lb->WidgetData->Parent, "", InitialLeft + lb->WidgetData->width - SCROLL_BUTTON_WIDTH, InitialTop + lb->WidgetData->height - SCROLL_BUTTON_HEIGHT, SCROLL_BUTTON_WIDTH, SCROLL_BUTTON_HEIGHT, 0, &(lb->DownArrow), ScrollDownFn, ButtonFlags, NULL);
  562.     lb->UD->ScrollDown->WidgetData->ParentControl = lb;
  563.  
  564.     lb->UD->ScrollUp = MakeButton(lb->WidgetData->Parent, "", InitialLeft + lb->WidgetData->width - SCROLL_BUTTON_WIDTH, InitialTop, SCROLL_BUTTON_WIDTH, SCROLL_BUTTON_HEIGHT, 0, &(lb->UpArrow), ScrollUpFn, ButtonFlags, NULL);
  565.     lb->UD->ScrollUp->WidgetData->ParentControl = lb;
  566.  
  567.     lb->UD->ScrollGadInfo.Flags = AUTOKNOB | FREEVERT | PROPNEWLOOK;
  568.     lb->UD->ScrollGadInfo.HorizBody = (unsigned short) -1;
  569.     lb->UD->ScrollGadInfo.VertBody = (unsigned short) -1;
  570.     lb->UD->ScrollGad.Width = SCROLL_BUTTON_WIDTH - 6;
  571.     lb->UD->ScrollGad.Activation = GACT_RELVERIFY | GACT_IMMEDIATE | GACT_FOLLOWMOUSE;
  572.     lb->UD->ScrollGad.GadgetType = GTYP_PROPGADGET;
  573.     lb->UD->ScrollGad.GadgetRender = &lb->UD->ScrollGadImage;
  574.     lb->UD->ScrollGad.SpecialInfo = &lb->UD->ScrollGadInfo;
  575.     lb->UD->ScrollGad.GadgetID = 1;
  576.     }
  577.  
  578. void OFFSET DisableScroller(Scroller *sc)
  579.     {
  580.     if (sc)
  581.         {
  582.         ListBox *lb = (ListBox*) sc->ScrollUp->WidgetData->ParentControl;
  583.         struct Window *win = lb->Win->Win;
  584.  
  585.         sc->ScrollUp->WidgetData->ParentControl = NULL;
  586.         sc->ScrollDown->WidgetData->ParentControl = NULL;
  587.         DisableButton(sc->ScrollUp);
  588.         DisableButton(sc->ScrollDown);
  589.         sc->ScrollUp->WidgetData->ParentControl = lb;
  590.         sc->ScrollDown->WidgetData->ParentControl = lb;
  591.  
  592.         if (GadInWinList(&sc->ScrollGad, win))
  593.             RemoveGadget(win, &sc->ScrollGad);
  594.         }
  595.     }
  596.  
  597. void OFFSET MakeHorizontalScroller(ListBox *lb, int (*ScrollLeftFn)(PushButton*), int (*ScrollRightFn)(PushButton*))
  598.     {
  599.     short ButtonWidthMult = 1, ButtonFlags = BN_CLEAR | BN_AR;
  600.     int InitialLeft, InitialTop;
  601.  
  602.     if (!lb)
  603.         return;
  604.     if (lb->LR)
  605.         return;
  606.     if ((lb->LR = (Scroller*) GuiMalloc(sizeof(Scroller), MEMF_CLEAR)) == NULL)
  607.         return;
  608.  
  609.     if (!ISGUIWINDOW(lb->WidgetData->Parent))
  610.         {
  611.         Frame *ParentFrame = (Frame *) lb->WidgetData->Parent;
  612.         InitialLeft = lb->WidgetData->left - ParentFrame->button.LeftEdge;
  613.         InitialTop = lb->WidgetData->top - ParentFrame->button.TopEdge;
  614.         }
  615.     else
  616.         {
  617.         InitialLeft = lb->WidgetData->left;
  618.         InitialTop = lb->WidgetData->top;
  619.         }
  620.  
  621.     if (lb->WidgetData->flags & SYS_LB_VSCROLL)
  622.         ButtonWidthMult = 2;
  623.     ResizeHorizontalScroller(lb, lb->WidgetData->left, lb->WidgetData->top, lb->WidgetData->width, lb->WidgetData->height, (double) 1.0, (double) 1.0, FALSE);
  624.     if (lb->UD)
  625.         lb->UD->DarkBorder.NextBorder = &lb->LR->LightBorder;
  626.     else
  627.         lb->DarkBorder.NextBorder = &lb->LR->LightBorder;
  628.     if (lb->hidden == 1)
  629.         ButtonFlags |= SYS_BN_HIDDEN;
  630.     lb->LR->ScrollDown = MakeButton(lb->WidgetData->Parent, "<", InitialLeft, InitialTop + lb->WidgetData->height - SCROLL_BUTTON_HEIGHT - 1, SCROLL_BUTTON_WIDTH, SCROLL_BUTTON_HEIGHT, 0, NULL, ScrollLeftFn, ButtonFlags, NULL);
  631.     lb->LR->ScrollDown->WidgetData->ParentControl = lb;
  632.  
  633.     lb->LR->ScrollUp = MakeButton(lb->WidgetData->Parent, ">", InitialLeft + lb->WidgetData->width - (ButtonWidthMult * SCROLL_BUTTON_WIDTH), InitialTop + lb->WidgetData->height - SCROLL_BUTTON_HEIGHT - 1, SCROLL_BUTTON_WIDTH, SCROLL_BUTTON_HEIGHT, 0, NULL, ScrollRightFn, ButtonFlags, NULL);
  634.     lb->LR->ScrollUp->WidgetData->ParentControl = lb;
  635.  
  636.     lb->LR->ScrollGadInfo.Flags = AUTOKNOB | FREEHORIZ | PROPNEWLOOK;
  637.     lb->LR->ScrollGadInfo.HorizBody = MAXBODY;
  638.     lb->LR->ScrollGadInfo.VertBody = MAXBODY;
  639.     lb->LR->ScrollGad.Height = SCROLL_BUTTON_HEIGHT - 4;
  640.     lb->LR->ScrollGad.Activation = GACT_RELVERIFY | GACT_IMMEDIATE | GACT_FOLLOWMOUSE;
  641.     lb->LR->ScrollGad.GadgetType = GTYP_PROPGADGET;
  642.     lb->LR->ScrollGad.GadgetRender = &lb->LR->ScrollGadImage;
  643.     lb->LR->ScrollGad.SpecialInfo = &lb->LR->ScrollGadInfo;
  644.     lb->LR->ScrollGad.GadgetID = 2;
  645.  
  646.     if (lb->hidden == 0)
  647.         {
  648.         AddGadget(lb->Win->Win, &lb->LR->ScrollGad, ~0);
  649.         RefreshGList(&lb->LR->ScrollGad, lb->Win->Win, NULL, 1);
  650.         DrawBorder(lb->Win->Win->RPort, &lb->LightBorder, 0, 0);
  651.         }
  652.     if (!lb->Enabled)
  653.         DisableScroller(lb->LR);
  654.     }
  655.  
  656. struct IntuiText OFFSET *SetLast(struct IntuiText *it)
  657.     {
  658.     if (it && it->NextText)
  659.         {
  660.         int top = it->TopEdge;
  661.         while (it->NextText && it->NextText->TopEdge == top)
  662.             it = it->NextText;
  663.         }
  664.     return it;
  665.     }
  666.  
  667. static struct IntuiText *NextEntry(struct IntuiText *it)
  668.     {
  669.     if (it)
  670.         {
  671.         int top = it->TopEdge;
  672.         while (it && it->TopEdge == top)
  673.             it = it->NextText;
  674.         }
  675.     return it;
  676.     }
  677.  
  678. void OFFSET SortITextList(struct IntuiText **FirstItem, int flags)
  679.     {
  680.    struct IntuiText *smallest, *start = *FirstItem, *ptr, *smallestprev, *startprev = NULL, *ptrprev;
  681.  
  682.    while (start)
  683.       {
  684.       smallest = start;
  685.       smallestprev = startprev;
  686.         ptr = NextEntry(start);
  687.       ptrprev = start;
  688.       while (ptr)
  689.          {
  690.             if (flags & NUM_ASCENDING || flags & NUM_DESCENDING)
  691.                 {
  692.                 if (atoi(flags & NUM_ASCENDING ? ptr->IText : smallest->IText)
  693.                     < atoi(flags & NUM_ASCENDING ? smallest->IText : ptr->IText))
  694.                     {
  695.                 smallest = ptr;
  696.                 smallestprev = ptrprev;
  697.                     }
  698.                 }
  699.             else if (flags & IGNORE_CASE)
  700.                 {
  701.                 if (stricmp(flags & ASCENDING ? ptr->IText : smallest->IText, flags & ASCENDING ?
  702.                         smallest->IText : ptr->IText) < 0)
  703.                     {
  704.                     smallest = ptr;
  705.                     smallestprev = ptrprev;
  706.                     }
  707.                 }
  708.             else if (strcmp(flags & ASCENDING ? ptr->IText : smallest->IText, flags & ASCENDING ?
  709.                     smallest->IText : ptr->IText) < 0)
  710.                {
  711.                smallest = ptr;
  712.                smallestprev = ptrprev;
  713.             }
  714.          ptrprev = ptr;
  715.             ptr = NextEntry(ptr);
  716.          }
  717.       if (smallest != start)
  718.          {
  719.             struct IntuiText *lsmallest, *lsmallestprev, *lstartprev;
  720.             lsmallest = SetLast(smallest);
  721.             lsmallestprev = SetLast(smallestprev);
  722.             lstartprev = SetLast(startprev);
  723.  
  724.          if (lsmallestprev)
  725.             lsmallestprev->NextText = lsmallest->NextText;
  726.          else
  727.             *FirstItem = lsmallest->NextText;
  728.          if (lstartprev)
  729.             lstartprev->NextText = smallest;
  730.          else
  731.             *FirstItem = smallest;
  732.          lsmallest->NextText = start;
  733.          }
  734.       else
  735.             start = NextEntry(start);
  736.       if (start == *FirstItem)
  737.          startprev = NULL;
  738.       else
  739.             {
  740.             int top;
  741.             startprev = *FirstItem;
  742.             while (startprev && startprev->NextText && startprev->NextText != start)
  743.                 startprev = startprev->NextText;
  744.             // Found the entry before the start one.  Now find the first one at this height
  745.             top = startprev->TopEdge;
  746.             startprev = *FirstItem;
  747.             while (startprev && startprev->TopEdge != top)
  748.                 startprev = startprev->NextText;
  749.             }
  750.       }
  751.     }
  752.  
  753. unsigned short OFFSET GetFontHeight(GuiWindow *win)
  754.     {
  755.     if (win)
  756.         {
  757.         struct TextAttr *font = win->ParentScreen->Font;
  758.  
  759.         if (font)
  760.             return font->ta_YSize;
  761.         else
  762.             return GuiFont.ta_YSize;
  763.         }
  764.     return 0;
  765.     }
  766.  
  767. /* Fakes an Input Event. If Window is Non NULL it will
  768.     clean its MessagePort. (W.D.L 900407) */
  769. void OFFSET FakeInputEvent(LONG eventclass, LONG Code, LONG Qualifier, LONG x, LONG y, struct Window *Window)
  770.     {
  771.     struct MsgPort *ioport;
  772.     struct IOStdReq *ioreq;
  773.     struct InputEvent event;
  774.  
  775.     memset(&event,0,sizeof (event));
  776.  
  777.     if (ioport = (struct MsgPort *)CreatePort("AV.TempPort", 0))
  778.         {
  779.         if (ioreq = (struct IOStdReq *)CreateStdIO(ioport))
  780.             {
  781.             if (!OpenDevice("input.device",0,(struct IORequest*) ioreq,0))
  782.                 {
  783.                 event.ie_Class = eventclass;
  784.                 event.ie_Code = Code;
  785.                 event.ie_X = x;
  786.                 event.ie_Y = y;
  787.                 event.ie_Qualifier = Qualifier;
  788.                 event.ie_NextEvent = NULL;
  789.                 ioreq->io_Command = IND_WRITEEVENT;
  790.                 ioreq->io_Length = sizeof(struct InputEvent);
  791.                 ioreq->io_Data = (APTR)&event;
  792.                 DoIO((struct IORequest*) ioreq);
  793.                 CloseDevice((struct IORequest*) ioreq);
  794.                 }
  795.             DeleteStdIO(ioreq);
  796.             }
  797.         DeletePort(ioport);
  798.         }
  799.  
  800. //    if (Window)
  801. //        CleanMsgPort(Window->UserPort);
  802.  
  803. } /* FakeInputEvent */
  804.  
  805. // Deactivates a string gadget by faking a Keydown/Keyup pair for the return key.
  806. void OFFSET DeActivateStrGad(void)
  807.     {
  808.     FakeInputEvent(IECLASS_RAWKEY, RAWKEY_RETURN, NULL, 0, 0, NULL);
  809.     FakeInputEvent(IECLASS_RAWKEY, RAWKEY_RETURN | IECODE_UP_PREFIX, NULL, 0, 0, NULL);
  810.     }
  811.  
  812. /* This function returns the topmost usable pixel in a window which has a title.  You can send a window
  813.     as a parameter or you can send a screen in which case the value returned will be correct for any
  814.     window opened in that screen which has a title. */
  815. int OFFSET TopWindowPixel(struct Screen *Screen, GuiWindow *Window)
  816.     {
  817.     if (Screen || Window)
  818.         {
  819.         struct Screen *sc = Screen;
  820.         if (!sc)
  821.             sc = Window->ParentScreen;
  822.         return sc->WBorTop + sc->Font->ta_YSize + 2;
  823.         }
  824.     return -1;
  825.     }
  826.  
  827. void OFFSET MakeBevel(struct Border *light, struct Border *dark, short *points, int left,
  828.         int top, int width, int height, BOOL raised)
  829.     {
  830.     int hicol, locol;
  831.  
  832.     if (raised)
  833.         {
  834.         hicol = Gui.HiPen;
  835.         locol = Gui.LoPen;
  836.         }
  837.     else
  838.         {
  839.         hicol = Gui.LoPen;
  840.         locol = Gui.HiPen;
  841.         }
  842.     light->DrawMode = dark->DrawMode = JAM1;
  843.     light->Count = dark->Count = 5;
  844.     light->LeftEdge = dark->LeftEdge = left;
  845.     light->TopEdge = dark->TopEdge = top;
  846.     light->FrontPen = hicol;
  847.     light->NextBorder = dark;
  848.     light->XY = points;
  849.     dark->FrontPen = locol;
  850.     dark->NextBorder = NULL;
  851.     dark->XY = &(points[10]);
  852.  
  853.     points[1] = points[2] = points[3] = points[4] = points[15] = 0;
  854.     points[6] = points[8] = points[9] = points[10] = points[17] = 1;
  855.     points[5] = points[11] = points[13] = height - 1;
  856.     points[7] = points[19] = height - 2;
  857.     points[12] = points[14] = width - 1;
  858.     points[0] = points[16] = points[18] = width - 2;
  859.     }
  860.  
  861. short downarrow[] = {4,3,7,6,10,3,11,3,8,6,5,3};
  862. short uparrow[] = {4,6,7,3,10,6,11,6,8,3,5,6};
  863.  
  864. void OFFSET MakeDownArrow(struct Border *arrow, int col)
  865.     {
  866.     arrow->DrawMode = JAM1;
  867.     arrow->Count = 6;
  868.     arrow->LeftEdge = arrow->TopEdge = 0;
  869.     arrow->FrontPen = col;
  870.     arrow->NextBorder = NULL;
  871.     arrow->XY = downarrow;
  872.     }
  873.  
  874. void OFFSET MakeUpArrow(struct Border *arrow, int col)
  875.     {
  876.     arrow->DrawMode = JAM1;
  877.     arrow->Count = 6;
  878.     arrow->LeftEdge = arrow->TopEdge = 0;
  879.     arrow->FrontPen = col;
  880.     arrow->NextBorder = NULL;
  881.     arrow->XY = uparrow;
  882.     }
  883.  
  884. void OFFSET FindScrollerValues(unsigned short total, unsigned short displayable,
  885.         unsigned short top, short overlap, unsigned short *body, unsigned short *pot)
  886.     {
  887.     unsigned short hidden = max(total - displayable, 0);
  888.  
  889.     if (overlap >= displayable)
  890.         overlap = displayable - 1;
  891.  
  892.     if (top > hidden)
  893.         top = hidden;
  894.  
  895.     *body = hidden > 0 ? (unsigned short) (((unsigned long) (displayable - overlap) * MAXBODY) / (total - overlap)) : MAXBODY;
  896.     *pot = hidden > 0 ? (unsigned short) (((unsigned long) top * MAXPOT) / hidden) : 0;
  897.     }
  898.  
  899. unsigned short OFFSET FindScrollerTop(unsigned short total, unsigned short displayable, unsigned short pot)
  900.     {
  901.     unsigned short top, hidden = max(total - displayable, 0);
  902.     top = (((unsigned long) hidden * pot) + (MAXPOT / 2)) >> 16;
  903.     return top;
  904.     }
  905.  
  906. void OFFSET AreaColFill(struct RastPort *rp, int left, int top, int width, int height, int col)
  907.     {
  908.     if (rp)
  909.         {
  910.         /*    Copy the RastPort's foreground pen and then set it equal to the specified
  911.             colour */
  912.         char FgPen = rp->FgPen;
  913.  
  914.         SetAPen(rp, (char) col);
  915.         // RectFill() the area and then reset the pens.
  916.         RectFill(rp, left, top, left + width - 1, top + height - 1);
  917.         SetAPen(rp, FgPen);
  918.         }
  919.     }
  920.  
  921. void OFFSET AreaBlank(struct RastPort *rp, int left, int top, int width, int height)
  922.     {
  923.     if (rp)
  924.         AreaColFill(rp, left, top, width, height, (int) rp->BgPen);
  925.     }
  926.  
  927. void OFFSET UnTruncateIText(struct IntuiText *IText, char *Original)
  928.     {
  929.     IText->IText = &Original[1]; // Reset the IText pointer (Original[0] is the trunc char)
  930.     if (Original[0])
  931.         {
  932.         Original[strlen(Original)] = Original[0]; // replace the trunc char in the main string
  933.         Original[0] = 0; // Clear the trunc char
  934.         }
  935.     }
  936.  
  937. void OFFSET TruncateIText(struct IntuiText *IText, char *Original, int MaxLen, int flags)
  938.     {
  939.     int len;
  940.  
  941.     UnTruncateIText(IText, Original);
  942.  
  943.     while ((len = strlen(IText->IText)) > 0 && IntuiTextLength(IText) > MaxLen)
  944.         {
  945.         if ((flags & JUSTIFY_RIGHT) || ((flags & JUSTIFY_CENTRE) && len % 2))
  946.             IText->IText = &(IText->IText[1]);
  947.         else
  948.             {
  949.             if (Original[0])
  950.                 IText->IText[len] = Original[0];
  951.             Original[0] = IText->IText[len - 1];
  952.             IText->IText[len - 1] = 0;
  953.             }
  954.         }
  955.     }
  956.  
  957. struct TextAttr OFFSET *CopyFont(struct TextAttr *font)
  958. {
  959.     struct TextAttr *copy = GuiMalloc(sizeof(struct TextAttr), 0);
  960.  
  961.     if (copy)
  962.     {
  963.         copy->ta_Name = GuiMalloc((strlen(font->ta_Name) + 1) * sizeof(char), 0);
  964.         if (copy->ta_Name)
  965.             strcpy(copy->ta_Name, font->ta_Name);
  966.         else
  967.         {
  968.             GuiFree(copy);
  969.             return NULL;
  970.         }
  971.         copy->ta_YSize = font->ta_YSize;
  972.         copy->ta_Style = font->ta_Style;
  973.         copy->ta_Flags = font->ta_Flags;
  974.     }
  975.     return copy;
  976. }
  977.